
FREECADPATH = '/usr/lib/freecad/lib'
import sys
sys.path.append(FREECADPATH)

import os
import random

import FreeCAD
import daysimfunc
import anticlockwise
from FreeCAD import Part
from FreeCAD import Base
from pybim import Building, Roof, Shade, Wall, Window, Zone, Floor




#THE CLASS WAS WRITTEN ASSUMING ALL THE POINTS ARE COMING IN ANTI-CLOCKWISE ARRANGEMENT WITH NORMAL POINTING OUTWARDS

ROOT = "/home/dexen/workspace/overhang_schema"


def face_vertexes_normals(faces):
    face_list = []
    normal_list = []
    
    for face in faces :
        if face:
            face_list.append([])
            normal_x =  face.normalAt(0,0).x
            normal_y =  face.normalAt(0,0).y
            normal_z =  face.normalAt(0,0).z
            normal = (normal_x,normal_y,normal_z)
            normal_list.append(normal)
            edges = face.Edges
            sort_edges = Part.__sortEdges__(edges)
            #sort thru all the edges and identify the common vertex
            for counter in range(len(sort_edges)):
                edge_pnts = []
                if counter < len(sort_edges)-1:
                    length = sort_edges[counter].Length
                    length2 = sort_edges[counter+1].Length
                    pnt_start1 = (round(sort_edges[counter].valueAt(0).x, 1),round(sort_edges[counter].valueAt(0).y, 1), 
                                  round(sort_edges[counter].valueAt(0).z,1))
                    
                    pnt_end1 = (round(sort_edges[counter].valueAt(length).x, 1), round(sort_edges[counter].valueAt(length).y, 1), 
                                round(sort_edges[counter].valueAt(length).z,1))
                    
                    pnt_start2 = (round(sort_edges[counter+1].valueAt(0).x,1), round(sort_edges[counter+1].valueAt(0).y, 1), 
                                  round(sort_edges[counter+1].valueAt(0).z, 1))
                    
                    pnt_end2 = (round(sort_edges[counter+1].valueAt(length2).x, 1), round(sort_edges[counter+1].valueAt(length2).y, 1), 
                                round(sort_edges[counter+1].valueAt(length2).z, 1))
                    
                    edge_pnts.append(pnt_start1)
                    edge_pnts.append(pnt_end1)
                    edge_pnts.append(pnt_start2)
                    edge_pnts.append(pnt_end2)
                    
                else:
                    length = sort_edges[counter].Length
                    length2 = sort_edges[0].Length
                    pnt_start1 = (round(sort_edges[counter].valueAt(0).x, 1),round(sort_edges[counter].valueAt(0).y, 1), 
                                  round(sort_edges[counter].valueAt(0).z, 1))
                    
                    pnt_end1 = (round(sort_edges[counter].valueAt(length).x, 1), round(sort_edges[counter].valueAt(length).y, 1), 
                                round(sort_edges[counter].valueAt(length).z, 1))
                    
                    pnt_start2 = (round(sort_edges[0].valueAt(0).x, 1), round(sort_edges[0].valueAt(0).y, 1), 
                                  round(sort_edges[0].valueAt(0).z, 1))
                    
                    pnt_end2 = (round(sort_edges[0].valueAt(length2).x, 1), round(sort_edges[0].valueAt(length2).y, 1), 
                                round(sort_edges[0].valueAt(length2).z, 1))
                    
                    edge_pnts.append(pnt_start1)
                    edge_pnts.append(pnt_end1)
                    edge_pnts.append(pnt_start2)
                    edge_pnts.append(pnt_end2)
            
                counterx =0
                for edge_pnt in edge_pnts:
                    for num_pnt in range(len(edge_pnts)):
                        if num_pnt <= len(edge_pnts) - (2+counterx):
                            if edge_pnt == edge_pnts[num_pnt + (1+counterx)]:
                                face_list[-1].append(edge_pnt)
                    counterx +=1
        else:
            face_list.append(None)
            normal_list.append(None)
            
    return face_list, normal_list 

def arrange_vertexes_anticlockwise(face_points, normal):
    anticlockwise_facelist = []
    counter = 0
    for point in face_points:
        if point:
            anticlockwise_facelist.append(anticlockwise.anticlockwise(point, normal[counter]))
        else:
            anticlockwise_facelist.append(None)
        counter +=1
        #print anticlockwise_facelist
    return anticlockwise_facelist
        
        
def generate_freecad(variables):
    import Part
    building_width = 15
    building_length = 15
    height = variables[4]
    num_walls = 4
    #make a box using the freecad part module 
    building = Part.makeBox(building_length,building_width,height)
    #apply transformation here as the rest of the scripts are base on the information from faces
    #
    faces = building.Faces
    #count thru the faces and sort the edges in continuous manner and get all the points
    wall_face_info = face_vertexes_normals(faces)
    #loop thru all the face and arrange all of them in anti-clockwise direction 
    wall_face_points = wall_face_info[0]
    wall_normal_list = wall_face_info[1]
    #this will turn the order of the vertexes into anti clockwise manner
    wall_anticlockwise_facelist = arrange_vertexes_anticlockwise(wall_face_points, wall_normal_list)
    #construct the windows    
    window_list = []
    for wall in range(num_walls):
        width = variables[wall + 5]
        if width > 0 :
            win_height = height - 1.5 
            #Make the window planes 
            window = Part.makePlane(win_height, width, Base.Vector(0,0,0), faces[wall].normalAt(0,0))
            cnt_win = window.CenterOfMass
            cnt_wall = faces[wall].CenterOfMass
            move_vector = (cnt_wall.x -cnt_win.x, cnt_wall.y -cnt_win.y, cnt_wall.z -cnt_win.z)
            trsf = Base.Matrix()
            trsf.move(Base.Vector(move_vector[0], move_vector[1], move_vector[2]))
            map_window = window.transformGeometry(trsf)
            window_face = map_window.Faces[0]
            window_list.append(window_face)
        else:
            window_list.append(None)
            
    
    win_face_info = face_vertexes_normals(window_list)
    win_face_points = win_face_info[0]
    win_normal_list = win_face_info[1]
    win_anticlockwise_facelist = arrange_vertexes_anticlockwise(win_face_points, win_normal_list)
    
    top_wires = []
    
    win_top = height - 0.75
    for win in window_list:
        if win:
            edges = win.Edges
            for edge in edges:
                edge_length = edge.Length
                if edge.valueAt(0).z == win_top and edge.valueAt(edge_length).z == win_top:
                    wire =  Part.Wire(edge)
                    top_wires.append(wire)
        else:
            top_wires.append(None)
     
    shade_list = []   
    for counter in range(num_walls):
        shade_width = variables[counter + 5]
        
        if shade_width > 0:
            depth = variables[counter]
            if depth > 0:
                mv_vector = (win_normal_list[counter][0]*depth,win_normal_list[counter][1]*depth,win_normal_list[counter][2]*depth)
                mv_vector = Base.Vector(mv_vector[0], mv_vector[1], mv_vector[2])
                wire_trsf = Base.Matrix()
                wire_trsf.move(mv_vector)
        
                top_wire2 = top_wires[counter].transformGeometry(wire_trsf)
                top_wire2 = top_wire2.Wires[0]

                shade = Part.makeLoft([top_wires[counter],top_wire2])
                shade = shade.Faces[0]
                Part.show(shade)
                shade_list.append(shade)
            else:
                shade_list.append(None)
        else:
            shade_list.append(None)
        
    
    shade_face_info = face_vertexes_normals(shade_list)
    shade_face_points = shade_face_info[0]
    shade_normal_list = shade_face_info[1]
    shade_anticlockwise_facelist = arrange_vertexes_anticlockwise(shade_face_points, shade_normal_list)
    
    return wall_anticlockwise_facelist, win_anticlockwise_facelist, shade_anticlockwise_facelist

def make_geometry(variables, building):
    #CREATE THE BUILDING AND ZONE OBJECT
    zone1 = Zone("zone1", building)
    #FLOOR CLASS 
    floor_list = generate_freecad(variables)[0][4]
    floor = Floor("FLOOR", floor_list)
    floor.material_idf("Heavy Floor")
    floor.material_rad("floor_mat")
    zone1.add_floor(floor)
    
    roof_list = generate_freecad(variables)[0][5]
    #ROOF CLASS
    roof = Roof("ROOF", roof_list)
    roof.material_idf("Heavy Roof/Ceiling")
    roof.material_rad("ceiling_mat")
    zone1.add_roof(roof)
            
    num_wall = 4
    wall_list = generate_freecad(variables)[0][0:4]
    wall_names = ["WALL1", "WALL2", "WALL3", "WALL4"]
    #GET THE WINDOWS POINTS
    for counter in range(num_wall):
        #WALL CLASS
        wall = Wall(wall_names[counter], wall_list[counter])
        wall.material_idf("Medium Exterior Wall")
        wall.material_rad("wall_mat")
        zone1.add_wall(wall)
        
            
        window_list = generate_freecad(variables)[1]
        win_names = ["WINDOW1", "WINDOW2",  "WINDOW3",  "WINDOW4"]
        if window_list[counter]:
            window = Window(win_names[counter], window_list[counter])
            window.material_idf("Sgl Clr 6mm")
            window.material_rad("window_glass")
            wall.add_window(window)
            
        
        shade_list = generate_freecad(variables)[2]
        #print shade_list 
        shading_names  = ["SHADING1", "SHADING2",  "SHADING3",  "SHADING4"]
        if shade_list[counter]:
            shade = Shade(shading_names[counter], shade_list[counter])
            shade.material_rad("shading_mat")
            wall.add_shade(shade)

#--------------------------------------------------------------------------------
#function for generating random height and width
def gmap(gene, values):
    step = 1.0 / len(values)
    for i in range(len(values)):
        if gene < (step + i*step):
            return values[i]
            
#--------------------------------------------------------------------------------
def generate_rad_file(variables, building):
    rad_data = []
    #WRITING THE RADIANCE FILE 
    #get the base file of the idf 
    #base_rad_file = os.path.join(ROOT,'radiancebasefile/overhangschema.rad')    

    #read all the stuff in the base idf file 
    #base_rad_file = open(base_rad_file,  "r")
    #base_rad_stuff = base_rad_file.read()
    #base_rad_file.close()

    #write the base file 
    #rad_data.append(base_rad_stuff)
    rad_geometry_data = building.rad(interior = True)
    rad_data.append(rad_geometry_data)
    return rad_data

def execute_radiance(rad_file_path):
    oconv_file = ROOT + "/radiancebasefile/rad_out/rad_results.oconv"
    sensors_file = ROOT + "/radiancebasefile/ovrhg_sensor_pt.pts"
    file = ROOT + "/radiancebasefile/rad_out/rad_results.txt"
    
    #create oconv file
    command1 = "oconv " + "'" + ROOT + "/radiancebasefile/overhangschema_mat.rad" + "'" + " "\
    + "'" + ROOT + "/radiancebasefile/overhangschema_sky.rad" + "'" + " " + "'" + rad_file_path + "'"\
    + " " + ">" + " " + "'" + oconv_file + "'"
    os.system(command1)
    
    #execute rtrace 
    command2 = "rtrace -h -w -I+ -ab 3 " + "'" +  oconv_file + "'" + " " + " < " + " " + "'" + sensors_file + "'"\
    + " " + " > " + " " + "'" + file + "'" 
    os.system(command2)
    
#--------------------------------------------------------------------------------  
def generate_idf_file(variables, building):
             
    idf_data = []
    
    #WRITING THE ENERGYPLUS FILE 
    #get the base file of the idf 
    base_idf_file = os.path.join( ROOT,'energyplusbasefile/overhangschema.idf')
    #read all the stuff in the base idf file 
    base_idf_file = open(base_idf_file,  "r")
    base_stuff = base_idf_file.read()
    base_idf_file.close()

    #write the base_stuff 
    idf_data.append(base_stuff)
    for _ in range(10):
        idf_data.append("! ======================================== \n")
        
    geometry_data = building.idf()
    idf_data.append(geometry_data)
    return idf_data
    
def execute_energyplus(idf_data,  path,  filename):
    
    idf_file_path = path + filename + ".idf"
    if not os.path.isdir(path):
        os.mkdir(path)
    idf_file = open(idf_file_path,  "w")
    for data in idf_data:
        idf_file.write(data)
    idf_file.close()
    
    #run energyplus simulation for each file 
    command = "runenergyplus '" +  idf_file_path + "' 'SGP_Singapore.486980_IWEC.epw'"
    os.system(command)
    
#--------------------------------------------------------------------------------
def generate_hea_file(path, filename, rad_file_path):
    
    #WRITING THE DAYSIM FILE 
    #get the base file of the hea
    base_hea_file = os.path.join(ROOT, 'daysimbasefile/overhangschema.hea')

    #read all the stuff in the base hea file 
    base_hea_file = open(base_hea_file,  "r")
    base_stuff = base_hea_file.read()
    base_hea_file.close()
    hea_data = []
    #create a foler to store daysim results / input the path as accordingly
    hea_data.append(base_stuff)
           
    #WRITE THE PROJ_INFO
    hea_file_path = path 
    tmp_dir = hea_file_path  + "tmp/"
    proj_info = daysimfunc.write_proj_info(filename, hea_file_path, tmp_dir)
    hea_data.append(proj_info)
    
    #WRITE THE WEATHER DATA 
    wea_file_dir = '/home/dexen/daysim/wea/SGP_Singapore.486980_IWEC_60min.wea' # in order to change the weather file input the 60min .wea dir, do the conversion from epw to wea using execute_epw2wea(bin_dir, source_file, destination_file)
    short_wea_name = "SGP_Singapore.486980_IWEC_5min.wea" # change the output .wea name respectively 
    weather_data = daysimfunc.write_weather_data(wea_file_dir, 0.2, short_wea_name)
    hea_data.append(weather_data)
    
    #WRITE THE GEOMETRY INFO

    sensor_file_dir = ROOT + '/radiancebasefile/ovrhg_sensor_pt.pts'
    rad_file_dir_list = [ROOT + "/radiancebasefile/overhangschema_mat.rad", 
                         rad_file_path]
    geometry_data = daysimfunc.write_geometry_info(filename, sensor_file_dir, rad_file_dir_list)
    hea_data.append(geometry_data)
    
    #WRITE THE RESULTS INFO
    result_data = daysimfunc.write_result_file(filename)
    hea_data.append(result_data)
    
    return hea_data
#------------------------------------------------------------------------------    
def create_rad_file(rad_data, path, filename):
    rad_file_path = path + filename + ".rad"
    if not os.path.isdir(path):
        os.mkdir(path)
    rad_file = open(rad_file_path,  "w")
    for data in rad_data:
        rad_file.write(data)
    rad_file.close()
    return rad_file_path
#---------------------------------------------------------------------------------
def execute_daysim(hea_data, path, filename, bin_dir ):
    #create hea file           
    hea_file_path = path + filename + ".hea"
    if not os.path.isdir(path):
        os.mkdir(path)
    hea_file = open(hea_file_path,  "w")
    for data in hea_data:
        hea_file.write(data)
    hea_file.close()
    #within the folder create 5 folder :pts rad res tmp wea 
    sub_hea_folders = ["pts", "rad", "res","tmp", "wea"]
    for folder in range(len(sub_hea_folders)):
        sub_hea_folder = sub_hea_folders[folder]
        sub_hea_folders_path = path + sub_hea_folder
        
        if not os.path.isdir(sub_hea_folders_path):
            os.mkdir(sub_hea_folders_path)
        if os.path.isdir(sub_hea_folders_path):
            files_in_dir = os.listdir(sub_hea_folders_path)
            for file in files_in_dir:
                os.remove(sub_hea_folders_path + "/" + file )
    
    command0 = os.chdir(bin_dir)
    command1 = "ds_shortterm " +  "'" + hea_file_path +"'"
    os.system(command1)
    command2 = "radfiles2daysim " +  "'" + hea_file_path +"'" + " -g -m -d"
    os.system(command2)
    command3 = "gen_dc " +  "'" + hea_file_path +"'"
    os.system(command3)
    command4 = "ds_illum " +  "'" + hea_file_path +"'" 
    os.system(command4)
    command5 = "gen_directsunlight " +  "'" + hea_file_path +"'" 
    os.system(command5)
    command6 = "ds_autonomy " +  "'" + hea_file_path +"'" 
    os.system(command6)
    
#---------------------------------------------------------------------------------
def random_genotype(size):
    genotype = []
    for _ in range(size):
        genotype.append(random.random())
    return genotype
#--------------------------------------------------------------------------------
def map_genotype(genotype):
    # 1st to 4th genes are shade lengths between 0 and 2
    # 5th gene is the height
    # 6th to 9th genes are window widths (0 to 15)
    building_heights = [2.5, 3, 3.5, 4, 4.5, 5]
    window_widths = range(0, 16)
    variables = []
    for i in range(0, 4):
        variables.append(round ((genotype[i]* 2), 1))
    variables.append(gmap(genotype[4] ,  building_heights))
    for i in range(5, 9):
        variables.append(gmap(genotype[i] , window_widths ))
    return variables
#--------------------------------------------------------------------------------
def process_da_result(path, filename):
    da_results = open(path + "res/" + filename  + ".da", "r")
    da_result_stuff = da_results.readlines()
    
    num_da_list = []
    
    #daysim results processing average the DA
    for line in range(len(da_result_stuff)-2): 
        lines =  da_result_stuff[2+line][78:83] 
        numbers_da = float(lines)
        num_da_list.append(round(numbers_da, 1))
    avg_da = sum(num_da_list) / len(num_da_list)
    avg_da = round(avg_da, 1)
    return avg_da

def process_illum_result(path, filename):
    illum_results = open(path + filename + ".txt", "r")
    illum_res_list = []
    for illum_result in illum_results:
        words  = illum_result.split()
        numbers = map(float, words)
        irradiance = round((0.265 * numbers[0]) + (0.670 * numbers[1]) + (0.065 * numbers[2]), 1)
        illuminance = irradiance * 179
        print illuminance
        illum_res_list.append(illuminance)
        
    total = sum(illum_res_list) 
    avg = round(total/len(illum_res_list), 1)
    print total 
    print avg
    return avg 
    
    
def process_energy_results(path,  filename):
    
    #energyplus results processing average the energytransfer 
    energy_results = open(path + "Output/"  + filename + "Meter.csv", "r")
    energy_list = []
    energy_results.next()
    for energy_result in energy_results:
        words = energy_result.split (",")
        numbers = map(float, words[1:])
        energy_list.append(numbers)
    total = (sum(energy_list[0]) + sum(energy_list[1])) / 22
    total = total * 2.77777777777778E-07 # convert from J to KWh
    return total
    

def write_results(results):
    result_filepath = os.path.join(ROOT, "overallresult/")
    if not os.path.isdir(result_filepath):
        os.mkdir(result_filepath)
    result_file = open( result_filepath  + "finalresult.csv",  "w")
    #write the results into a csv file 
    result_file.write("project_name,daylight_autonomy(%),total_energy_transfer(kWh)(daily_avg)\n")
    result_file.write(results)
    result_file.close()

def develop(genotype):
    variables = map_genotype(genotype)
    print variables 
    building = Building()
    _ = make_geometry(variables, building)
    rad_file = generate_rad_file(variables, building)
    idf_file = generate_idf_file(variables, building)
    return rad_file,  idf_file
    
def eval_energy(idf_data):
    path = ROOT + "/energyplusbasefile/ep_out/"
    filename  = "results"
    execute_energyplus(idf_data,  path,  filename)
    result = process_energy_results(path,  filename)
    return result
    
def eval_daylight(rad_data):
    path = ROOT + "/daysimbasefile/daysim_out/"
    filename  = "da_results"
    bin_dir = "/home/dexen/daysim/bin"
    rad_path = ROOT + "/radiancebasefile/rad_out/"
    rad_filename  = "rad_results"
    rad = create_rad_file(rad_data, rad_path, rad_filename)
    hea_file = generate_hea_file(path, filename, rad)
    execute_daysim(hea_file, path, filename, bin_dir)
    result = process_da_result(path, filename)
    return result 

def eval_daylight_rad(rad_data):
    
    path = ROOT + "/radiancebasefile/rad_out/"
    filename  = "rad_results"
    rad = create_rad_file(rad_data, path, filename)
    execute_radiance(rad)
    result = process_illum_result(path, filename)
    return result 
    
def setup():
    source_file =  '/home/dexen/daysim/wea/USA_HI_Barbers.Point.NAS.911780_TMY3.epw'
    destination_file = '/home/dexen/daysim/wea/USA_HI_Barbers.Point.NAS.911780_TMY3_60min.wea'
    bin_dir = "/home/dexen/daysim/bin"
    daysimfunc.execute_epw2wea(bin_dir, source_file, destination_file)

#--------------------------------------------------------------------------------
# MAIN SCRIPT
#--------------------------------------------------------------------------------
random.seed()
#CONVERT EPW TO WEA
#setup()

num_of_design = 50
results = ""
for counter in range(num_of_design):
    #generate the variables for the developmental script
    genotype = random_genotype(9)
    #developmental script
    rad_idf_data = develop(genotype)
    
    #evaluation
    daylight = eval_daylight_rad(rad_idf_data[0])
    energy = eval_energy(rad_idf_data[1])
    results = results + str(counter) + ","+ str(daylight) + "," +  str(energy) +"\n" 
    print "done"
    
#write the results into a csv file 
write_results(results)
print "FINISH "







